///\todo upgrade

//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#ifndef _BUOLA_UTILITY_UDELEGATE_H_
#define _BUOLA_UTILITY_UDELEGATE_H_

#include <buola/buola.h>

namespace buola {

template<typename tDelegate>
class UDelegate;

template<typename tType>
class delegate_ptr
{
public:
    ///initializes the pointer
    ///
    ///Unless \p pAdd is \c false, the reference count of the object is incremented.
    delegate_ptr(tType *pPtr=nullptr)
        :   mPtr(pPtr)
    {
        if(mPtr)
        {
            mPrev=nullptr;
            mNext=mPtr->mFirstDelegatePtr;
            mPtr->mFirstDelegatePtr=this;
            if(mNext)
                mNext->mPrev=this;
        }
    }

    ///initialization from an intrusive pointer to a compatible type
    delegate_ptr(const delegate_ptr &pRH)
        :   delegate_ptr(pRH.mPtr)
    {}

    delegate_ptr(delegate_ptr &&pRH)
    {
        mPtr=pRH.mPtr;
        pRH.mPtr=nullptr;
        if(mPtr)
        {
            mNext=pRH.mNext;
            mPrev=pRH.mPrev;
            if(mNext)
                mNext->mPrev=this;
            if(mPrev)
                mPrev->mNext=this;
            else
                mPtr->mFirstDelegatePtr=this;
        }
    }
    
    ///destructor
    ///
    ///Decrements the reference count of the object.
    ~delegate_ptr()
    {
        if(mPtr)
        {
            if(mNext) mNext->mPrev=mPrev;
            if(mPrev)
                mPrev->mNext=mNext;
            else
                mPtr->mFirstDelegatePtr=mNext;
        }
    }
    
    ///assignment operator
    delegate_ptr &operator=(tType *pPtr)
    {
        if(pPtr==mPtr) return *this;
        if(mPtr)
        {
            if(mNext) mNext->mPrev=mPrev;
            if(mPrev)
                mPrev->mNext=mNext;
            else
                mPtr->mFirstDelegatePtr=mNext;
        }
        mPtr=pPtr;
        if(mPtr)
        {
            mPrev=nullptr;
            mNext=mPtr->mFirstDelegatePtr;
            mPtr->mFirstDelegatePtr=this;
            if(mNext)
                mNext->mPrev=this;
        }
        return *this;
    }

    delegate_ptr &operator=(const delegate_ptr &pRH)
    {
        return operator=(pRH.mPtr);
    }
    
    ///returns a reference to the pointed-to object
    tType &operator*() const
    {
        return *mPtr;
    }
    
    ///returns a raw pointer to the pointed-to object
    tType *operator->() const
    {
        return mPtr;
    }
    
    ///returns a raw pointer to the pointed-to object
    tType *get() const
    {
        return mPtr;
    }
    
    ///explicit conversion to bool
    ///
    ///Returns true if the pointer points to some object.
    explicit operator bool() const
    {
        return mPtr;
    }
    
private:
    tType *mPtr;
    delegate_ptr *mNext;
    delegate_ptr *mPrev;
    friend UDelegate<tType>;
};

template<typename tDelegate>
class UDelegate
{
public:
    UDelegate()
        :   mFirstDelegatePtr(nullptr)
    {}
    
    virtual ~UDelegate()
    {
        while(mFirstDelegatePtr)
        {
            mFirstDelegatePtr->mPtr=nullptr;
            mFirstDelegatePtr=mFirstDelegatePtr->mNext;
        }
    }
    
private:
    friend delegate_ptr<tDelegate>;
    delegate_ptr<tDelegate> *mFirstDelegatePtr;
};

/*namespace buola*/ }

#endif
